package com.qhnu.zxjx.configuration;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * @description: master主数据源配置类
 * @author: panpei
 * @date: 2019/11/13 13:38
 * @version: 1.0
 **/
@Configuration
// 扫描 Mapper 接口并容器管理
@MapperScan(basePackages = DataSourceZxjxConfiguration.PACKAGE, sqlSessionFactoryRef = "zxjxSqlSessionFactory")
public class DataSourceZxjxConfiguration {

	private Logger logger = LogManager.getLogger(getClass());

	/**
	 * 精确到 invest 目录，以便跟其他数据源隔离
	 * Mapper接口目录
	 */
	static final String PACKAGE = "com.qhnu.zxjx.dao.zxjx";

	/**
	 * Mapper文件目录
	 */
	static final String MAPPER_LOCATION = "classpath:mapper/zxjx/*.xml";

	/**
	 * 全局配置文件
	 */
	static final String CONFIG_LOCATION = "classpath:mybatis-config.xml";

	@Value("${spring.datasource.druid.zxjx.url}")
	private String url;

	@Value("${spring.datasource.druid.zxjx.username}")
	private String user;

	@Value("${spring.datasource.druid.zxjx.password}")
	private String password;

	@Value("${spring.datasource.druid.zxjx.driver-class-name}")
	private String driverClass;

	@Value("${spring.datasource.druid.zxjx.init-size}")
	private Integer initSize;

	@Value("${spring.datasource.druid.zxjx.min-idle}")
	private Integer minIdle;

	@Value("${spring.datasource.druid.zxjx.max-active}")
	private Integer maxActive;

	@Value("${spring.datasource.druid.zxjx.max-wait}")
	private Long maxWait;

	@Value("${spring.datasource.druid.zxjx.time-between-eviction-runs-millis}")
	private Long timeBetweenEvictionRunsMillis;

	@Value("${spring.datasource.druid.zxjx.validation-query}")
	private String validationQuery;

	@Value("${spring.datasource.druid.zxjx.test-while-idle}")
	private Boolean testWhileIdle;

	@Value("${spring.datasource.druid.zxjx.test-on-borrow}")
	private Boolean testOnBorrow;

	@Value("${spring.datasource.druid.zxjx.test-on-return}")
	private Boolean testOnReturn;

	@Value("${spring.datasource.druid.zxjx.min-evictable-idle-time-millis}")
	private Long minEvictableIdleTimeMillis;

	@Value("${spring.datasource.druid.zxjx.max-evictable-idle-time-millis}")
	private Long maxEvictableIdleTimeMillis;

	@Value("${spring.datasource.druid.zxjx.keep-alive}")
	private Boolean keepAlive;

	@Value("${spring.datasource.druid.zxjx.pool-prepared-statements}")
	private Boolean poolPreparedStatements;

	@Value("${spring.datasource.druid.zxjx.max-pool-prepared-statement-per-connection-size}")
	private Integer maxPoolPreparedStatementPerConnectionSize;

	@Value("${spring.datasource.druid.zxjx.filters}")
	private String filters;

	@Value("${spring.datasource.druid.zxjx.connection-properties}")
	private String connectionProperties;

	@Bean(name = "zxjxDataSource")
	@Primary
	public DataSource dataSource() {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setDriverClassName(driverClass);
		dataSource.setUrl(url);
		dataSource.setUsername(user);
		dataSource.setPassword(password);
		// 配置初始化大小、最小、最大
		dataSource.setInitialSize(initSize);
		dataSource.setMinIdle(minIdle);
		dataSource.setMaxActive(maxActive);
		// 配置获取连接等待超时的时间
		dataSource.setMaxWait(maxWait);
		// 验证连接有效与否的SQL
		dataSource.setValidationQuery(validationQuery);
		// 设置从连接池获取连接时是否检查连接有效性，true时，如果连接空闲时间超过minEvictableIdleTimeMillis进行检查，否则不检查;false时，不检查
		dataSource.setTestWhileIdle(testWhileIdle);
		// 设置从连接池获取连接时是否检查连接有效性，true时，每次都检查;false时，不检查
		dataSource.setTestOnBorrow(testOnBorrow);
		// 设置往连接池归还连接时是否检查连接有效性，true时，每次都检查;false时，不检查
		dataSource.setTestOnReturn(testOnReturn);
		// 配置间隔多久启动一次DestroyThread，对连接池内的连接才进行一次检测，单位是毫秒。
		// 检测时:1.如果连接空闲并且超过minIdle以外的连接，如果空闲时间超过minEvictableIdleTimeMillis设置的值则直接物理关闭。2.在minIdle以内的不处理。
		dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
		// 配置一个连接在池中最小生存的时间，单位是毫秒
		dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
		// 配置一个连接在池中最大生存的时间，单位是毫秒
		dataSource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
		// 打开后，增强timeBetweenEvictionRunsMillis的周期性连接检查，minIdle内的空闲连接，每次检查强制验证连接有效性. 参考：https://github.com/alibaba/druid/wiki/KeepAlive_cn
		dataSource.setKeepAlive(keepAlive);
		// 打开PSCache，并且指定每个连接上PSCache的大小
		dataSource.setPoolPreparedStatements(poolPreparedStatements);
		dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
		try {
			dataSource.setFilters(filters);
		} catch (SQLException e) {
			logger.error("druid configuration initialization filter: " + e);
		}
		dataSource.setConnectionProperties(connectionProperties);
		return dataSource;
	}

	@Bean(name = "zxjxTransactionManager")
	@Primary
	public DataSourceTransactionManager transactionManager() {
		return new DataSourceTransactionManager(dataSource());
	}

	@Bean(name = "zxjxSqlSessionFactory")
	@Primary
	public SqlSessionFactory sqlSessionFactory(@Qualifier("zxjxDataSource") DataSource dataSource) throws Exception {
		final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource);
		sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(CONFIG_LOCATION));
		sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
		return sessionFactory.getObject();
	}
}